package com.ljx.seckill.util;

import cn.hutool.core.util.RandomUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @Author: LiJunXi
 * @Description: 单机版Redis分布式锁
 * 存在问题：
 * 1、过期时间小于执行时间
 * @Date: 2025-01-10  22:26
 */
public class RedisUtilV4 implements RedisLock{

    StringRedisTemplate stringRedisTemplate;
    private String UUID;
    private DefaultRedisScript<Boolean> redisScript;
    private DefaultRedisScript<Boolean> expireScript;
    private boolean isRetry = false;

    public RedisUtilV4(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.UUID = RandomUtil.randomString(5);
        this.redisScript = new DefaultRedisScript<>();
        this.redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/Test.lua")));
        this.redisScript.setResultType(Boolean.class);
        this.expireScript = new DefaultRedisScript<>();
        this.expireScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/expireTimeAdd.lua")));
        this.expireScript.setResultType(Boolean.class);
    }

    public Boolean lock(String key, long time, TimeUnit timeUnit) {
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, UUID + Thread.currentThread().getId(), time, timeUnit);
        if (result) {
            new Thread(() -> {
                while(isRetry) {
                    // 开启轮询，若没有解锁就一直添加过期时间
                    stringRedisTemplate.execute(expireScript, Arrays.asList(key), UUID + Thread.currentThread().getId(), "30");
                }
            }).start();
        }
        return result;
    }

    public Boolean unlock(String key) {
        Boolean result = stringRedisTemplate.execute(redisScript, Arrays.asList(key), UUID + Thread.currentThread().getId());
        if (result) {
            isRetry = false;
        }
        return result;
    }

}